#!/usr/bin/env python

'''
gpscp -- scp to multiple hosts at once

Usage: gpscp [--version] [-?v] [-r] [-p port] [-u user]
             [-h host] [-f hostfile] [-J host_substitution_character] [[user@]host1:]file1 [...] [[user@]hosts2:]file2

	     --version    : print version information
             -?           : print this help screen
	     -v	          : verbose mode
             -r           : recursively copy entire directories
	     -h host      : ssh host to connect to (multiple -h is okay)
	     -f file      : a file listing all hosts to connect to
             -J character : character to be substitute as hostname [default='=']
'''

# disable deprecationwarnings
import warnings

warnings.simplefilter('ignore', DeprecationWarning)

import os
import sys
import getopt
import popen2
from gppylib.util import ssh_utils
from gppylib.gpparseopts import OptParser
from gppylib.parseutils import canonicalize_address

progname = os.path.split(sys.argv[0])[-1]

if sys.version_info < (2, 5, 0):
    sys.exit(
        '''Error: %s is supported on Python versions 2.5 or greater
        Please upgrade python installed on this machine.''' % progname)


#
# all the command line options
#
class Global:
    script_name = os.path.split(__file__)[-1]
    USER = os.environ.get('LOGNAME') or os.environ.get('USER')
    opt = {}
    opt['-v'] = False
    opt['-h'] = []
    opt['-f'] = None
    opt['-J'] = '=:'
    opt['-r'] = False
    filePath = []


GV = Global()


################
def usage(exitarg):
    parser = OptParser()
    try:
        parser.print_help()
    except:
        print __doc__
    sys.exit(exitarg)


#############
def print_version():
    print '%s version $Revision$' % GV.script_name
    sys.exit(0)


#############
def parseCommandLine():
    try:
        (options, args) = getopt.getopt(sys.argv[1:], '?vrJ:p:u:h:f:', ['version'])
    except Exception, e:
        usage('Error: ' + str(e))

    for (switch, val) in options:
        if (switch == '-?'):
            usage(0)
        elif (switch == '-v'):
            GV.opt[switch] = True
        elif (switch == '-f'):
            GV.opt[switch] = val
        elif (switch == '-h'):
            GV.opt[switch].append(val)
        elif (switch == '-J'):
            GV.opt[switch] = val + ':'
        elif (switch == '-r'):
            GV.opt[switch] = True
        elif (switch == '--version'):
            print_version()

    hf = (len(GV.opt['-h']) and 1 or 0) + (GV.opt['-f'] and 1 or 0)
    if hf != 1:
        usage('Error: please specify at least one of -h or -f args, but not both')

    if (len(args) < 2):
        usage('Error: please specify local and remote file paths')

    GV.filePath = args


#############
def run(cmd, peer):
    if GV.opt['-v']: print '[INFO]', cmd
    p = popen2.Popen4(cmd)
    p.x_cmd = cmd
    p.x_peer = peer
    p.tochild.close()
    return p


#############
try:
    parseCommandLine()
    hostlist = ssh_utils.HostList()
    for h in GV.opt['-h']:
        hostlist.add(h)
    if GV.opt['-f']:
        hostlist.parseFile(GV.opt['-f'])

    try:
        hostlist.checkSSH()
    except ssh_utils.SSHError, e:
        sys.exit('[ERROR] ' + str(e))

    GV.opt['-h'] = hostlist.filterMultiHomedHosts()
    if len(GV.opt['-h']) == 0:
        usage('Error: missing hosts in -h and/or -f arguments')

    scp = 'scp -o "BatchMode yes" -o "StrictHostKeyChecking no"'
    if GV.opt['-r']:  scp += ' -r'

    proc = []
    for peer in GV.opt['-h']:
        peer = canonicalize_address(peer)  # MPP-13617
        cmd = scp + ' '
        for f in GV.filePath:
            cmd += f.replace(GV.opt['-J'], '%s:' % peer) + ' '
        proc.append(run(cmd, peer))

    errmsg = None
    for p in proc:
        for out in p.fromchild:
            print '[OUT %s] %s' % (p.x_peer, out)
        status = p.wait()
        if status:
            errmsg = '[ERROR %s] exit %d, cmd - %s' % (p.x_peer, status, p.x_cmd)
            print errmsg

    if errmsg: sys.exit(1)
    if GV.opt['-v']: print '[INFO] completed successfully'

except KeyboardInterrupt:
    sys.exit('\nInterrupted...')
